/*
 *	MIDAS Context Based Routing
 *	cbr_acache.cc
 *	Copyright (c) 2007 Bartlomiej Zolnierkiewicz
 *	MEAG IT PW
 */

#include <arpa/inet.h> // for inet_addr()
#include <semaphore.h>

#include "scheduler.h" // for packet.h
#include "packet.h" // for IP_BROADCAST

#include "cbr.h"
#include "cbr_acache.h"

sem_t ip_cache_sem;
sem_t mac_cache_sem;

/**
 * Add the new mapping to the CBR->IP mapping cache.
 *
 * @param id CBR id
 * @param ip IP address
 */

void cbr_IPCache::insert(cbr_ID id, ip_addr ip)
{
	mappings_list::const_iterator i;

	sem_wait(&ip_cache_sem);
	for (i = mappings.begin(); i != mappings.end(); i++) {
		// this assumes that address doesn't change
		if ((*i)->getID() == id) {
			sem_post(&ip_cache_sem);
			return;
		}
	}

	cbr_log("adding ID->IP mapping: %d->%s\n", id, iptostr(&ip));
	cbr_IPMapping *mapping = new cbr_IPMapping(id, ip);
	mappings.push_front(mapping);
	sem_post(&ip_cache_sem);
}

/**
 * Search the CBR->IP mapping cache for the existing mapping.
 *
 * @param id CBR id
 *
 * @return IP address (255.255.255.255 if no mapping exists).
 */

const ip_addr cbr_IPCache::lookup(cbr_ID id) const
{
	ip_addr ip = inet_addr("255.255.255.255");
	mappings_list::const_iterator i;

	sem_wait(&ip_cache_sem);
	for (i = mappings.begin(); i != mappings.end(); i++) {
		if ((*i)->getID() == id) {
			ip_addr ip = (*i)->getIP();
			sem_post(&ip_cache_sem);
			cbr_log("found ID->IP mapping: %d->%s\n",
				id, iptostr(&ip));
			return ip;
		}
	}
	sem_post(&ip_cache_sem);

	return ip;
}

/**
 * Add the new mapping to the CBR->MAC mapping cache.
 *
 * @param id CBR id
 * @param mac MAC address
 */

void cbr_MACCache::insert(cbr_ID id, mac_addr mac)
{
	mappings_list::const_iterator i;

	sem_wait(&mac_cache_sem);
	for (i = mappings.begin(); i != mappings.end(); i++) {
		// this assumes that address doesn't change
		if ((*i)->getID() == id) {
			sem_post(&mac_cache_sem);
			return;
		}
	}

	cbr_log("adding ID->MAC mapping: %d->%s\n", id, mactostr(&mac));
	cbr_MACMapping *mapping = new cbr_MACMapping(id, mac);
	mappings.push_front(mapping);
	sem_post(&mac_cache_sem);
}

/**
 * Search the CBR->MAC mapping cache for the existing mapping.
 *
 * @param id CBR id
 *
 * @return MAC address (FF:FF:FF:FF:FF:FF) if no mapping exists.
 */

const mac_addr cbr_MACCache::lookup(cbr_ID id) const
{
	//char *macstr = "FF:FF:FF:FF:FF:FF";
	char *macstr = "00:00:00:00:00:00";
	mac_addr mac;
	convertstrtomac(macstr, &mac);
	mappings_list::const_iterator i;

	sem_wait(&mac_cache_sem);
	for (i = mappings.begin(); i != mappings.end(); i++) {
		if ((*i)->getID() == id) {
			mac = (*i)->getMAC();
			sem_post(&mac_cache_sem);
			return mac;
		}
	}
	sem_post(&mac_cache_sem);

	return mac;
}

/**
 * Initialize CBR->IP mapping cache.
 *
 * @param agent CBR agent
 */

void cbr_IPCache::init(CBR *agent)
{
	// broadcast ID (-1 == 0xffffffff)
	char *b_ipstr = "255.255.255.255";
	ip_addr b_ip = inet_addr(b_ipstr);

	sem_init(&ip_cache_sem, 0, 1);

	insert(IP_BROADCAST, b_ip);

	// Central Node ID
	if (agent->cn.getIP() != 0)
		insert(agent->cn.getID(), agent->cn.getIP());
}

/**
 * Initialize CBR->MAC mapping cache.
 *
 * @param agent CBR agent
 */

void cbr_MACCache::init(CBR *agent)
{
	// broadcast ID (-1 == 0xffffffff)
	char *b_macstr = "FF:FF:FF:FF:FF:FF";
	mac_addr b_mac;
	convertstrtomac(b_macstr, &b_mac);

	sem_init(&mac_cache_sem, 0, 1);

	insert(IP_BROADCAST, b_mac);

	// Central Node operates only in IP mode...
	// ... unless in testing mode
	//insert(agent->cn.getID(), agent->cn.getMAC());
}

/**
 * Purge CBR->IP mapping cache.
 */

void cbr_IPCache::purge(void)
{
	mappings_list::iterator i, j;

	sem_wait(&ip_cache_sem);
	for (i = mappings.begin(); i != mappings.end();) {
		j = i;
		i++;
		delete (*j);
		mappings.erase(j);
	}
	sem_post(&ip_cache_sem);

	sem_destroy(&ip_cache_sem);
}

/**
 * Purge CBR->MAC mapping cache.
 */

void cbr_MACCache::purge(void)
{
	mappings_list::iterator i, j;

	sem_wait(&mac_cache_sem);
	for (i = mappings.begin(); i != mappings.end();) {
		j = i;
		i++;
		delete (*j);
		mappings.erase(j);
	}
	sem_post(&mac_cache_sem);

	sem_destroy(&mac_cache_sem);
}
